﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

using LowLevelGraphics.Drawing;

namespace LowLevelGraphics.Filter
{

    /// <summary>
    /// This filter is created completely on my own.
    /// Normally one could use a "Connected-Component" Filter, but this is a little bit different.
    /// This filter will detect lines.
    /// You may combine vertical/horizontal mode to detect bresenham lines.
    /// 
    /// So to put it under a nutshell this is a kind of line detection filter.
    /// </summary>
    public class ConnectedPixelLines : BaseImageFilter
    {
        protected DIRECTION_MODE M_DIRECTION_MODE = DIRECTION_MODE.VERTICAL;
        protected bool m_bDetectOnly = false;
        protected Size m_SizeTolerance = Size.Empty;
        protected float m_fTolerance = 0.0f;
        //protected
        /// <summary>
        /// Initializes a new instance of the <see cref="ConnectedPixelLines"/> class.
        /// </summary>
        public ConnectedPixelLines()
        {
            this.M_PROCESSING_MODE = PROCESSING_MODE.ToIntRgb;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ConnectedPixelLines"/> class.
        /// </summary>
        /// <param name="_connectedPixelLines">The _connected pixel lines.</param>
        internal ConnectedPixelLines(ConnectedPixelLines _connectedPixelLines)
        {
            this.M_DIRECTION_MODE = _connectedPixelLines.M_DIRECTION_MODE;
            this.m_bDetectOnly = _connectedPixelLines.m_bDetectOnly;
            this.m_SizeTolerance = _connectedPixelLines.m_SizeTolerance;
            this.m_fTolerance = _connectedPixelLines.m_fTolerance;
        }

        /// <summary>
        /// Determines whether a bresenham match is applied to the found pixel clouds.
        /// </summary>
        public bool DetectOnly
        {
            get { return m_bDetectOnly; }
            set { m_bDetectOnly = value; }
        }

        /// <summary>
        /// Gets or sets the direction mode.
        /// </summary>
        /// <value>The direction mode.</value>
        public DIRECTION_MODE DirectionMode
        {
            get { return M_DIRECTION_MODE; }
            set { M_DIRECTION_MODE = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            ColorCalculation calc = Definitions.ColorCalculationDictionaryProcessingMode[M_PROCESSING_MODE];

            //First version will be totally unoptimized
            int x = 0;
            int y = 0;
            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Height;
            Color color = Color.Transparent;
            List<KeyValuePair<Point, float>> aPoint = null;

            UnsafeGraphics graphics = new UnsafeGraphics(_bitmap);

            for (y = 1; y < nHeight - 1; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    switch(M_DIRECTION_MODE)
                    {
                        case DIRECTION_MODE.HORIZONTAL:
                            aPoint = GetHorizontalLinePixels(calc, _bitmap, x, y, nWidth, nHeight);
                            break;

                        case DIRECTION_MODE.VERTICAL:
                            aPoint = GetVerticalLinePixels(calc, _bitmap, x, y, nWidth, nHeight);
                            break;
                    }
                    if (!m_bDetectOnly)
                    {
                        int nCount = MatchBresenham(_bitmap, calc, aPoint);
                        for (int i = 0; i < nCount; i++)
                        {
                            _bitmap.SetPixel(aPoint[i].Key.X, aPoint[i].Key.Y, Color.Blue);
                        }
                    }
                }
            }
            return _bitmap;
        }

        /// <summary>
        /// Matches the bresenham.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="_calc">The _calc.</param>
        /// <param name="_aPoint">The _a point.</param>
        /// <returns></returns>
        protected int MatchBresenham(UnsafeBitmap _bitmap, ColorCalculation _calc, List<KeyValuePair<Point, float>> _aPoint)
        {
            if (_aPoint.Count == 0) return -1;
            Point ptStart = _aPoint[0].Key;
            Point ptEnd = _aPoint[_aPoint.Count - 1].Key;

            int nCount = 0;
            foreach (Point pt in Bresenham.RenderLine(ptStart, ptEnd))
            {
                foreach (KeyValuePair<Point, float> aTemp in _aPoint)
                {
                    if (aTemp.Key == pt)
                    {
                        nCount++;
                    }
                }
            }
            return nCount;
        }

        /// <summary>
        /// Gets the vertical line pixels.
        /// </summary>
        /// <param name="_calc">The _calc.</param>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="nWidth">Width of the n.</param>
        /// <param name="nHeight">Height of the n.</param>
        /// <returns></returns>
        protected List<KeyValuePair<Point, float>> GetVerticalLinePixels(ColorCalculation _calc, UnsafeBitmap _bitmap, int x, int y, int nWidth, int nHeight)
        {
            bool bContinue = false;
            List<KeyValuePair<Point, float>> aColorPoint = new List<KeyValuePair<Point, float>>();
            int nXStart = x;
            float fValueStraight = 0.0f;
            float fValueLeft = 0.0f;
            float fValueRight = 0.0f;
            float fValue = 0.0f;
            float mathAbsStraight = 0.0f;
            float mathAbsLeft = 0.0f;
            float mathAbsRight = 0.0f;
            bool bFound = true;

            for (int nYStart = y; nYStart < nHeight; nYStart++)
            {
                //read point, and next below and the left/right point next to the below point
                fValue = _calc(_bitmap.GetPixel(nXStart, nYStart));
                fValueStraight = _calc(_bitmap.GetPixel(nXStart, nYStart + 1));
                fValueLeft = _calc(_bitmap.GetPixel(nXStart - 1, nYStart + 1));
                fValueRight = _calc(_bitmap.GetPixel(nXStart + 1, nYStart + 1));

                //calculate the distance value PROCESSING_MODE dependant.
                mathAbsStraight = Math.Abs(fValue - fValueStraight);
                mathAbsLeft = Math.Abs(fValue - fValueLeft);
                mathAbsRight = Math.Abs(fValue - fValueRight);

                //compare to tolerance.
                if (mathAbsStraight <= m_fTolerance)
                {
                    bFound = true;
                    aColorPoint.Add(new KeyValuePair<Point, float>(new Point(nYStart, nXStart), fValueStraight));
                }
                else if (mathAbsLeft <= m_fTolerance)
                {
                    nXStart -= 1;
                    bFound = true;
                    aColorPoint.Add(new KeyValuePair<Point, float>(new Point(nYStart, nXStart), fValueLeft));
                }
                else if (mathAbsRight <= m_fTolerance)
                {
                    nXStart += 1;
                    bFound = true;
                    aColorPoint.Add(new KeyValuePair<Point, float>(new Point(nYStart, nXStart), fValueRight));
                }
                else
                {
                    bFound = false;
                }
            }
            return aColorPoint;
        }

        /// <summary>
        /// Gets the horizontal line pixels.
        /// </summary>
        /// <param name="_calc">The _calc.</param>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="nWidth">Width of the n.</param>
        /// <param name="nHeight">Height of the n.</param>
        /// <returns></returns>
        protected List<KeyValuePair<Point,float>> GetHorizontalLinePixels(ColorCalculation _calc, UnsafeBitmap _bitmap, int x, int y, int nWidth, int nHeight)
        {
            Point[] aPoint = new Point[2];
            return null;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new ConnectedPixelLines(this);
        }
    }
}

